part of YFree;

final yPages = <YPage>[];

typedef YPage _OnCreateState();

enum YIntentType { Push, Present, Push_NoSlide, Push_Replace, Present_Replace, Push_NoSlide_Replace, Root_Replace }

class YStatefulWidget extends StatefulWidget {
  _OnCreateState onCreateState;

  YStatefulWidget(this.onCreateState);

  @override
  State<StatefulWidget> createState() => onCreateState();
}

var yIsShowProgress = false;

///页面基类
abstract class YPage extends State<StatefulWidget> {
  late YPage yPage;
  var yIsAlive = true;
  Map<String, dynamic> _broadcastReceivers = {};
  BuildContext? _dialogContext;

  @override
  void initState() {
    super.initState();
    yPage = this;
    MethodChannel(runtimeType.toString()).setMethodCallHandler((methodCall) async {
      if (methodCall.method == "onYReceiveBroadcast") {
        var arguments = methodCall.arguments;
        yOnReceiveBroadcast(arguments["broadcastName"], arguments["broadcastParams"]);
      } else
        return await yOnYFreeCall(methodCall.method, methodCall.arguments);
    });
    yRun(() {
      yOnCreate();
      yOnPostCreate();
    }, delay: 0);
  }

  void yOnPostCreate() {}

  @override
  void dispose() {
    yFinish(pop: false);
    yOnDestory();
    yIsAlive = false;
    super.dispose();
  }

  void yOnCreate();

  void yOnDestory() {}

  bool ySafeAreaBottom() => true;

  Future<dynamic> yOnYFreeCall(String method, Map<dynamic, dynamic> arguments) {
    return Future.value(null);
  }

  void yOnReceiveBroadcast(String broadCastName, Map<dynamic, dynamic> arguments) {}

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(value: YConfig.statubarTextTheme.copyWith(statusBarColor: YConfig.statusBarColor), child: YCIMGestureDetector(context, Scaffold(backgroundColor: Colors.white, body: SafeArea(top: false, bottom: ySafeAreaBottom(), child: WillPopScope(child: yBuild(context), onWillPop: yIsShowProgress ? () => Future.value(false) : null)))));
  }

  Widget yBuild(BuildContext context);

  // dismissible为false时，要调用yDismissDialog_unDismissible关闭对话框
  void yShowDialog({required alertBuilder(BuildContext pageContext, BuildContext? dialogContext), bool dismissible = true}) {
    showDialog(
      context: yPage.context,
      barrierDismissible: dismissible,
      builder: (dialogContext) {
        if (!dismissible) this._dialogContext = dialogContext;
        return WillPopScope(
          onWillPop: () => Future.value(dismissible),
          child: alertBuilder(yPage.context, dismissible ? dialogContext : null),
        );
      },
    );
  }

  // dismissible为false时，要调用yDismissDialog_unDismissible关闭对话框
  void yShowStatefulDialog({required alertBuilder(BuildContext pageContext, BuildContext? dialogContext, BuildContext statefulContext, StateSetter setDialogState), bool dismissible = true}) {
    showDialog(
      context: yPage.context,
      barrierDismissible: dismissible,
      builder: (dialogContext) {
        if (!dismissible) this._dialogContext = dialogContext;
        return WillPopScope(
          onWillPop: () => Future.value(dismissible),
          child: StatefulBuilder(
            builder: (statefulContext, setDialogState) => alertBuilder(yPage.context, dismissible ? dialogContext : null, statefulContext, setDialogState),
          ),
        );
      },
    );
  }

  void yDismissDialog_unDismissible() {
    if (this._dialogContext != null) {
      var state = Navigator.of(this._dialogContext!);
      if (state.canPop()) state.pop();
      this._dialogContext = null;
    }
  }

  void yDismissDialog(BuildContext? dialogContext) {
    if (dialogContext != null) {
      var state = Navigator.of(dialogContext);
      if (state.canPop()) state.pop();
    }
  }

  ///弹出对话框
  void yShowBuilder(String message, confirmCallBack(), cancelCallBack()?, bool cancelAble, {String posTitle = "确定", String negTitle = "取消"}) async {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (context) {
        return WillPopScope(
          onWillPop: () => Future.value(false),
          child: AlertDialog(
            title: Text("${YConfig.appName}提示", style: TextStyle(color: Colors.black)),
            content: SingleChildScrollView(child: Text(message, style: TextStyle(color: Colors.black))),
            actions: [
              YFlatButton(Text(posTitle, style: TextStyle(color: Colors.black)), onClick: () {
                confirmCallBack();
                Navigator.of(context).pop();
              }),
              Offstage(
                offstage: !cancelAble && cancelCallBack == null,
                child: YFlatButton(
                  Text(negTitle, style: TextStyle(color: Colors.black)),
                  onClick: cancelCallBack != null
                      ? () {
                          cancelCallBack();
                          Navigator.of(context).pop();
                        }
                      : () {
                          Navigator.of(context).pop();
                        },
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  ///弹出actionsheet
  void yShowActionSheet(String message, List<String> actions, onCallBack(int index, String action)) async {
    final result = await showModalActionSheet<String>(
      context: context,
      actions: actions.map<SheetAction<String>>((e) => SheetAction(key: e, label: e)).toList(),
      isDismissible: true,
      useRootNavigator: true,
      cancelLabel: "取消",
      message: message,
      title: "请选择",
    );
    var i = 0;
    int? index;
    actions.forEach((element) {
      if (element == result) index = i;
      i++;
    });
    onCallBack(index!, result!);
  }

  ///弹出单选框
  void yShowSelectBuilder(String message, bool cancelAble, List<String> actions, onCallBack(int index, String action), {onCancelBack()?}) async {
    final result = await showConfirmationDialog(
      context: context,
      message: message,
      title: "请选择",
      cancelLabel: cancelAble ? "取消" : null,
      actions: actions.map<AlertDialogAction<String>>((e) => AlertDialogAction(key: e, label: e)).toList(),
      useRootNavigator: true,
      barrierDismissible: false,
      okLabel: "确定",
    );
    var i = 0;
    int? index;
    actions.forEach((element) {
      if (element == result) index = i;
      i++;
    });
    onCallBack(index!, result!);
  }

  void yPost<T extends dynamic>(String jkid, Map? kv, yAjaxCallback(bool isSuccess, List<T>? results, String code, String message), {onModel(map)?, showProgress = true, showErrorToast = true, cacheEnable = false}) async {
    if (cacheEnable) {
      var cacheResult = await yRead(yGetPostCacheKey(jkid, kv));
      if (cacheResult != null) {
        Map<dynamic, dynamic> result = json.decode(cacheResult);
        _doYPostResult(jkid, kv, result, yAjaxCallback, onModel, showProgress, showErrorToast, cacheEnable, true);
        _doYPost(jkid, kv, yAjaxCallback, onModel, false, showErrorToast, true);
        return;
      }
    }
    _doYPost(jkid, kv, yAjaxCallback, onModel, showProgress, showErrorToast, cacheEnable);
  }

  void _doYPost<T extends dynamic>(String jkid, Map? kv, yAjaxCallback(bool isSuccess, List<T>? results, String code, String message), onModel(map)?, bool showProgress, bool showErrorToast, bool cacheEnable) async {
    Map<dynamic, dynamic> result;
    if (showProgress) setState(() => yIsShowProgress = true);
    try {
      result = await _post(jkid, kv, showProgress, showErrorToast);
    } catch (e) {
      if (showErrorToast) yToast(e.toString());
      yAjaxCallback(false, null, "y_999", e.toString());
      return;
    }
    if (showProgress) setState(() => yIsShowProgress = false);
    _doYPostResult(jkid, kv, result, yAjaxCallback, onModel, showProgress, showErrorToast, cacheEnable, false);
  }

  Future<Map<dynamic, dynamic>> _post(String jkid, Map? kv, bool showProgress, bool showErrorToast) async {
    final dio = Dio();
    if (!yIsWeb) dio.options.connectTimeout = YConfig.timeOut;
    dio.options.receiveTimeout = YConfig.timeOut;
    dio.options.sendTimeout = YConfig.timeOut;
    dio.options.headers.addAll(await YConfig.onHttpHeader());
    dio.options.contentType = "application/x-www-form-urlencoded";
    if (!yIsWeb)
      (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
        client.badCertificateCallback = (cert, host, port) => true;
        if (YConfig.httpProxy != "") client.findProxy = (uri) => "PROXY ${YConfig.httpProxy}";
      };
    try {
      yLog(jkid + ":request:" + (kv == null || kv.isEmpty ? "无入参" : yToJson(kv)));
      if (showProgress) yShowProgress();
      Map<String, dynamic> data = {"jkid": jkid, "jkparam": yToJson(kv ?? {})};
      data.addAll(await YConfig.onHttpParam());
      var response = await dio.post(YConfig.IP, queryParameters: data);
      if (showProgress) yDismissProgress();
      if (response.statusCode != 200) {
        yLog(jkid + ":responseFailure:" + "网络请求异常！！！\tErrorNo:" + response.statusCode.toString() + "\tMsg:" + response.statusMessage!);
        if (showErrorToast) yToast(YConfig.DEBUG ? "网络请求异常！！！\nErrorNo:" + response.statusCode.toString() + "\nMsg:" + response.statusMessage! : "网络请求错误，请重试");
        return {"code": response.statusCode, "results": null, "message": response.statusMessage};
      }
      yLog(jkid + ":responseSuccess:" + yToJson(response.data));
      try {
        final resMap = response.data;
        final root = resMap["root"];
        if (root["code"] == "200") {
          return {"code": root["code"], "results": root["result"], "message": root["message"]};
        } else if (root["code"] == "-1001") {
          return {"code": root["code"], "results": root["result"], "message": root["message"]};
        } else {
          if (showErrorToast) yToast(YConfig.DEBUG ? "网络请求回执message：\n" + root["message"] : root["message"]);
          return {"code": root["code"], "results": null, "message": root["message"]};
        }
      } on Exception {
        final msg = YConfig.DEBUG ? "网络请求回执message解析异常，响应字符串：\n" + response.data.toString() : "网络请求错误，请重试";
        if (showErrorToast) yToast(msg);
        return {"code": "", "results": null, "message": msg};
      }
    } on Exception catch (e) {
      if (showProgress) yDismissProgress();
      yLog(jkid + ":responseFailure:" + "网络请求异常！！！\tErrorNo:" + "" + "\tMsg:" + e.toString());
      if (showErrorToast) yToast(YConfig.DEBUG ? "网络请求异常！！！\nErrorNo:" + "" + "\nMsg:" + e.toString() : "网络请求错误，请重试");
      return {"code": "", "results": null, "message": e.toString()};
    }
  }

  void _doYPostResult<T extends dynamic>(String jkid, Map? kv, Map<dynamic, dynamic> result, yAjaxCallback(bool isSuccess, List<T>? results, String code, String message), onModel(map)?, bool showProgress, bool showErrorToast, bool cacheEnable, bool isFromCache) {
    switch (result["code"]) {
      case "200":
        var isBadData = false;
        List<dynamic> results = [];
        try {
          results = yIsWeb || yIsPC ? result["results"] : json.decode(result["results"]);
          List<T> values;
          if (onModel == null)
            values = results as List<T>;
          else
            values = results.map((m) => onModel(m) as T).toList();
          try {
            if (yIsAlive) yAjaxCallback(true, values, result["code"], result["message"]);
          } catch (e) {
            isBadData = true;
            if (showErrorToast) yToast(e.toString());
          }
        } catch (e) {
          isBadData = true;
          if (showErrorToast) yToast(e.toString());
          if (yIsAlive) yAjaxCallback(false, null, "y_999", e.toString());
        }
        if (cacheEnable && results.isNotEmpty && !isBadData) {
          if (!isFromCache) ySave(yGetPostCacheKey(jkid, kv), json.encode(result));
        } else
          yDelete(yGetPostCacheKey(jkid, kv));
        break;
      default:
        if (result["code"] == YConfig.yPostCommomErrorCode) {
          yDelete(yGetPostCacheKey(jkid, kv));
          YConfig.onYPostCommonError.call(yPage, result["code"], result["message"], jkid, kv, (isSuccess, results, code, message) {
            yAjaxCallback(isSuccess, results == null ? null : results.map((e) => e as T).toList(), code, message);
          }, onModel, showProgress, showErrorToast, cacheEnable);
        } else {
          yDelete(yGetPostCacheKey(jkid, kv));
          if (yIsAlive) yAjaxCallback(false, null, result["code"], result["message"]);
        }
        break;
    }
  }

  String yGetPostCacheKey(String jkid, Map? kv) {
    return "ypost_${jkid}_${json.encode(kv)}";
  }

  /*
    // 分页示例
    var _page = 1;
    List<Model> models = [];
    Future<void> loadData({isAdd = false}) async {
      _page = isAdd ? _page + 1 : 1;
      return yPost<Model>("", {
        "page": _page,
      }, (isSuccess, results, code, message) async {
        _page = isSuccess ? (isAdd ? (results.isEmpty ? _page - 1 : _page) : _page) : (isAdd ? _page - 1 : _page);
        if (isSuccess) {
          if (!isAdd) models.clear();
          setState(() => models.addAll(results));
        }
      }, onModel: (m) => Model.fromJson(m), showProgress: !isAdd);
    }
  */

  void yIntent(YPage page, {YIntentType intentType = YIntentType.Push, bool popToPage = false, bool reCreateWhenPopToPage = true}) {
    yRun(() {
      if (popToPage) {
        var hadPage = false;
        yPages.forEach((item) {
          if (item.runtimeType == page.runtimeType) hadPage = true;
        });
        if (hadPage)
          for (var i = yPages.length - 1; i >= 0; i--) {
            var curPage = yPages[i];
            if (curPage.runtimeType == page.runtimeType) {
              if (reCreateWhenPopToPage) {
                curPage.yFinish();
                break;
              } else
                return;
            } else {
              curPage.yFinish();
            }
          }
      } else
        for (var i = 0; i < yPages.length; i++) if (yPages[i].runtimeType == page.runtimeType) return;
      yPages.add(page);
      switch (intentType) {
        case YIntentType.Root_Replace: // 替换
          Navigator.of(yPage.context).pushAndRemoveUntil(CupertinoPageRoute(builder: (context) => YStatefulWidget(() => page)), (route) => route == null);
          break;
        case YIntentType.Present: // 模态
          Navigator.push(
            yPage.context,
            PageRouteBuilder(
              transitionDuration: Duration(milliseconds: 300),
              pageBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2) => YStatefulWidget(() => page),
              transitionsBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2, Widget widget) => SlideTransition(position: Tween<Offset>(begin: Offset(0.0, 1.0), end: Offset(0.0, 0.0)).animate(CurvedAnimation(parent: animtion1, curve: Curves.fastOutSlowIn)), child: widget),
            ),
          );
          break;
        case YIntentType.Push: // 压栈
          Navigator.push(yPage.context, CupertinoPageRoute(builder: (context) => YStatefulWidget(() => page)));
          break;
        case YIntentType.Push_NoSlide: // 压栈，无手势返回
          Navigator.push(
            yPage.context,
            PageRouteBuilder(
              transitionDuration: Duration(milliseconds: 300),
              pageBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2) => YStatefulWidget(() => page),
              transitionsBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2, Widget widget) => SlideTransition(position: Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0)).animate(CurvedAnimation(parent: animtion1, curve: Curves.ease)), child: widget),
            ),
          );
          break;
        case YIntentType.Present_Replace: // 抽取替换，模态
          yFinish();
          Navigator.push(
            yPage.context,
            PageRouteBuilder(
              transitionDuration: Duration(milliseconds: 300),
              pageBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2) => YStatefulWidget(() => page),
              transitionsBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2, Widget widget) => SlideTransition(position: Tween<Offset>(begin: Offset(0.0, 1.0), end: Offset(0.0, 0.0)).animate(CurvedAnimation(parent: animtion1, curve: Curves.fastOutSlowIn)), child: widget),
            ),
          );
          break;
        case YIntentType.Push_Replace: // 抽取替换，压栈
          yFinish();
          Navigator.push(yPage.context, CupertinoPageRoute(builder: (context) => YStatefulWidget(() => page)));
          break;
        case YIntentType.Push_NoSlide_Replace: // 抽取替换，压栈，无手势返回
          yFinish();
          Navigator.push(
            yPage.context,
            PageRouteBuilder(
              transitionDuration: Duration(milliseconds: 300),
              pageBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2) => YStatefulWidget(() => page),
              transitionsBuilder: (BuildContext context, Animation<double> animtion1, Animation<double> animtion2, Widget widget) => SlideTransition(position: Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0)).animate(CurvedAnimation(parent: animtion1, curve: Curves.ease)), child: widget),
            ),
          );
          break;
      }
    });
  }

  void yFinish({bool pop = true}) {
    for (var i = yPages.length - 1; i >= 0; i--)
      if (yPages[i] == yPage) {
        yPages.removeAt(i);
        if (pop) {
          Navigator.pop(yPage.context);
          yBroadcast_unregisters();
        }
        break;
      }
  }

  void yFinishToHome() {
    for (var i = yPages.length - 1; i > 0; i--) yPages[i].yFinish();
  }

  void yBroadcast_register(String action, callback(Map<String, dynamic>? map)) {
    _broadcastReceivers[action] = callback;
  }

  void yBroadcast_send(String action, {Map<String, dynamic>? params}) {
    yPages.forEach((yPage) {
      if (yPage._broadcastReceivers.containsKey(action))
        try {
          yPage._broadcastReceivers[action](params);
        } catch (e) {}
    });
  }

  void yBroadcast_unregister(String action) {
    _broadcastReceivers.remove(action);
  }

  void yBroadcast_unregisters() {
    try {
      _broadcastReceivers.forEach((key, value) => yBroadcast_unregister(key));
    } catch (e) {}
    _broadcastReceivers.clear();
  }

  GestureDetector YCIMGestureDetector(BuildContext context, Widget child) {
    return GestureDetector(child: Container(width: yScreenSize(context).width, height: yScreenSize(context).height, child: child), onTap: () => yCloseInputMethod());
  }

  Widget YAppBar({String? title, Color? navBarTitleColor, bool canBack = true, bool divider = false, Widget? left, Widget? right, Widget? widget, String? statubar, String? navBar}) {
    var screenSize = yScreenSize(yPage.context);
    double statubarHeight = yStatubarHeight(yPage.context);
    double navBarHeight = yNavBarHeight();
    return Container(
      child: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Image.asset(statubar ?? YConfig.statubar, width: screenSize.width, height: statubarHeight, fit: BoxFit.fill),
          Stack(children: <Widget>[
            Image.asset(navBar ?? YConfig.navBar, width: screenSize.width, height: navBarHeight, fit: BoxFit.fill),
            Container(
              height: navBarHeight,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.max,
                children: widget == null
                    ? <Widget>[
                        Container(width: navBarHeight, height: navBarHeight, child: left ?? (canBack ? YFlatButton(Icon(Icons.arrow_back_ios, color: navBarTitleColor ?? YConfig.navBarTitleColor), onClick: () => yFinish()) : YEmpty())),
                        YTitleWidget(title, navBarTitleColor: navBarTitleColor),
                        Container(width: navBarHeight, height: navBarHeight, child: right ?? YEmpty()),
                      ]
                    : <Widget>[Container(width: screenSize.width, height: navBarHeight, child: widget)],
              ),
            ),
          ]),
          divider ? Divider(height: 1, color: Colors.grey.shade400) : YEmpty(),
        ],
      ),
    );
  }
}

///页面基类(保存状态)
abstract class YKeepAlivePage extends YPage with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return AnnotatedRegion<SystemUiOverlayStyle>(value: YConfig.statubarTextTheme.copyWith(statusBarColor: YConfig.statusBarColor), child: YCIMGestureDetector(context, Scaffold(backgroundColor: Colors.white, body: SafeArea(top: false, bottom: ySafeAreaBottom(), child: WillPopScope(child: yBuild(context), onWillPop: yIsShowProgress ? () => Future.value(false) : null)))));
  }

  Widget yBuild(BuildContext context);
}
/*
  MyWidget(context, title: testValue).yWidget, onClick: () => setState(() => testValue = "new test");

  var testValue = "test";

  class MyWidget extends YWidget {
    MyWidget(
      BuildContext context, {
      this.title,
    }) : super(context);

    String? title;

    @override
    Widget yBuild(BuildContext context) {
      yToast(yScreenSize(context).width.toString());
      return Text(title ?? "");
    }
  }
*/

abstract class YWidget extends YPage {
  YWidget(this.context);

  late BuildContext context;

  Widget get yWidget => _YBaseWidget(child: yBuild(context));

  @override
  void yOnCreate() {}

  @override
  Widget build(BuildContext context) => YEmpty();
}

class _YBaseWidget extends StatefulWidget {
  const _YBaseWidget({
    Key? key,
    required this.child,
  }) : super(key: key);

  final Widget child;

  @override
  _YBaseWidgetState createState() => _YBaseWidgetState();
}

class _YBaseWidgetState extends State<_YBaseWidget> {
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}
